<?php

// http://www.sil.org/iso639-3/iso-639-3_Latin1_20100707.tab
// http://www.ethnologue.com/codes/LanguageCodes.tab

define('VKLANG_INIT_LOADED', 1);

class VKLangInit {


	const CONF_TAB = "\t";



	static $OP_MAP = array('<' => VKLang::LT_INDEX, '>' => VKLang::GT_INDEX, '=' => VKLang::EQ_INDEX, '<>' => VKLang::NE_INDEX);
	static $OBJ_MAP = array('original' => VKLang::ORIGINAL_OBJ, 'current' => VKLang::CURRENT_OBJ);



static function initParser($locales_file) {
  $locales_str = "";
  $files = scandir(VKLANG_CONF_DIR);
  $languages = array();
  foreach ($files as $file) {
    list($filename, $ext) = explode('.', $file);
    if ($filename && $ext == 'json') {
      $language = $filename;
      $country = '';
      if (strpos($filename, '_') !== false) {
        list($language, $country) = explode('_', $filename, 2);
      }
      $languages[$language] = $filename;
      if ($country) $languages[$language.'_'.strtolower($country)] = $filename;
      $locales_str .= "'".$filename."',";
    }
  }
  if ($locales_str) $locales_str = substr($locales_str, 0, -1);
  $locales_str = "<?php\n\$VKLangConfig['locales'] = array({$locales_str});\n";
  
  $f = fopen(VKLANG_CONF_DIR.'/'.VKLANG_CONF_LANGS_CSV, 'r');
  $n = 0;
  $exist_langs = array();
  
  while (($line = fgets($f, 4096)) !== false) {
    $n++;
    if ($n == 1) continue;
    list($country_id, $iso1, $iso3, $priority) = explode("\t", $line);
    if (isset($languages[$iso3.'_'.$country_id])) $exist_langs[$country_id][] = $languages[$iso3.'_'.$country_id];
    if ($iso1 && isset($languages[$iso1.'_'.$country_id])) $exist_langs[$country_id][] = $languages[$iso1.'_'.$country_id];
    if (isset($languages[$iso3])) $exist_langs[$country_id][] = $languages[$iso3];
    if ($iso1 && isset($languages[$iso1])) $exist_langs[$country_id][] = $languages[$iso1];
  }

  fclose($f);
  
  $langs_str = '';
  foreach ($exist_langs as $country_id => $langs) {
    $langs_str .= "'{$country_id}' => array('".implode("','", $langs)."'),\n";
  }
  $locales_str .= "\$VKLangConfig['country_locales'] = array(\n{$langs_str});\n";
  file_put_contents($locales_file, $locales_str);
}



static function toEncArray($arr) {
  foreach ($arr as $key => $val) {
    if (is_array($val)) {
      $arr[$key] = self::toEncArray($arr[$key]);
    } else if (is_string($val)) {
      $arr[$key] = self::toEnc($arr[$key]);
    }
  }
  return $arr;
}



static function parseConfig($json_file, $php_file) {


  $json = self::toEncArray(json_decode(file_get_contents($json_file), true));
  
  $res = array();
  $res['cached_time'] = array();
  
  foreach ($json['rules'] as $name => $rule) {
    switch ( $rule['type'] ) {
      case 'time':
        list($rule, $res['cached_time']) = self::parseDateRule($rule, $res['cached_time']);
        $res['rules'][$name] = $rule;
        break;
      case 'flex':
        if (!isset($rule['group'])) {
          $rule = self::parseFlexRule($rule);
          if ($rule === false) die('error');
          $res['rules'][$name] = $rule;
        } else {
          $group_names = explode(',', $rule['group']);
          unset($rule['group']);
          foreach ($group_names as $i => $group_name) {
            $group_rule = self::parseFlexRule($rule, $i);
            if ($group_rule === false) die('error');
            $res['rules'][$group_name] = $group_rule;
            
          }
        }
        
        break;
      case 'switch':
        $variants = array();
        $c = 0;
        foreach ($rule['variants'] as $variant) {
          preg_match("/^(.*?[^<>=])(<>|<|>|=)([^<>=].*)$/", $variant['cond'], $match);
          $variants[] = array(array(self::$OP_MAP[$match[2]], $match[1], $match[3]), $variant['variant']);
        }
        $rule['variants'] = $variants;
        
      default:
        # code...
        $res['rules'][$name] = $rule;
        break;
    }
  }
  
  $configs = $json['config'];
  foreach ($configs as $conf_name => &$config) {
    if ($config['type'] == 'time' && isset($rule['subpatterns'])) {
      foreach ($rule['subpatterns'] as $subpattern_name => $subpattern) {
        foreach ($subpattern['rules'] as $sub_rule_name => $sub_rule) {
          if (strpos($sub_rule_name, '-') !== false) {
            list($sub_start, $sub_end) = explode('-', $sub_rule_name, 2);
            for ($i = $sub_start; $i <= $sub_end; $i++) {
              $config['subpatterns'][$subpattern_name][$i] = $sub_rule;
            }
            unset($config['subpatterns'][$subpattern_name][$sub_rule_name]);
          }
        }
      }
    }
  }
  
  $res['config'] = $configs;
  $json = "<?php\n\$VKLangConfig['config'] = ".self::serializeArray($res).";";
  file_put_contents($php_file, $json);
}



static function parseDateRule($rule, $cached_time) {
  foreach ($rule['patterns'] as $i => $patt_rule) {
    if (!is_array($patt_rule)) continue;
    $arr = array();
    if ($patt_rule[1] === null) {
      $arr[0] = -((1<<31) - 1);
    } else {
      switch ($patt_rule[0]) {
        case 'd': $arr[0] = $patt_rule[1] ? "{$patt_rule[1]} day 0:00" : "today 0:00"; break;
        case 'y': $arr[0] = $patt_rule[1] ? "{$patt_rule[1]} year 1 january" : "this year 1 january"; break;
      }
    }
    if ($patt_rule[2] === null) {
      $arr[1] = 1<<31;
    } else {
      switch ($patt_rule[0]) {
        case 'd': $arr[1] = $patt_rule[2] ? "{$patt_rule[2]} day 0:00" : "today 0:00"; break;
        case 'y': $arr[1] = $patt_rule[2] ? "{$patt_rule[2]} year 1 january" : "this year 1 january"; break;
      }
    }
    if (is_string($arr[0])) $cached_time[$arr[0]] = false;
    if (is_string($arr[1])) $cached_time[$arr[1]] = false;
    $arr[2] = $patt_rule[3];
    $rule['patterns'][$i] = $arr;
  }
  return array($rule, $cached_time);
}



static function parseFlexRule($rule, $group_index = false) {

  
  if (is_string($rule['from'])) $rule['from'] = array($rule['from']);
  foreach ($rule['from'] as $i => $rule_from) {
    preg_match_all("/\{(original|current)\.(.*?)\}/", $rule_from, $matches, PREG_SET_ORDER);
    if ($matches) {
      foreach ($matches as $match) {
        $rule['from_obj'][$i][self::$OBJ_MAP[$match[1]]][] = $match[2];
      }
    }
  }

  if (isset($rule['to'])) {
    if (is_string($rule['to'])) $rule['to'] = array($rule['to']);
    foreach ($rule['to'] as $i => $rule_to) {
      preg_match("/\{(original|current)\.(.*?)\}/", $rule_to, $match);
      if ($match) {
        $rule['to_obj'][$i] = array(self::$OBJ_MAP[$match[1]], $match[2]);
      }
    }
  }

  $conditions = array();
  $c = 1;
  if (isset($rule['patterns'])) {
    foreach ($rule['patterns'] as &$pattern) {
      foreach ($pattern['replace'] as &$replace) {
        if ($group_index !== false) {
          $outs = explode(',', $replace['out']);
          $replace['out'] = isset($outs[$group_index]) ? $outs[$group_index] : '';
        }
        if (isset($replace['cond']) && !isset($conditions[$replace['cond']])) $conditions[$replace['cond']] = $c++;
      }
    }
    if ($conditions) {
      
      foreach ($conditions as $cond_str => $index) {
        preg_match("/^(original|current)\.(.*?[^<>=])(<>|<|>|=)([^<>=].*)$/", $cond_str, $match);
        $rule['conditions'][$index] = array(self::$OP_MAP[$match[3]], self::$OBJ_MAP[$match[1]], $match[2], $match[4]);
      }
      
    }
    $patterns = array();
    $j = 0;
    foreach ($rule['patterns'] as $pattern) {
      foreach ($pattern['replace'] as $replace) {
        //$pattern['replace'][$i] = array(intval($conditions[$replace['cond']]), $replace['out']);
        $searches = explode(',', $pattern['search']);
        $letter = '~';
        foreach ($searches as $search) {
          $pos1 = strpos($search, '~');
          $pos2 = ($pos1 !== false) ? strpos($search, '~', $pos1 + 1) : false;
          $search = str_replace('~', '', $search);
          $pat_len = strlen($search);
          $key = $pat_len * 100000 + $j;
          $j++;
          $out = $replace['out'];
          $pos3 = strpos($out, '~');
          $pos4 = ($pos3 !== false) ? strpos($out, '~', $pos3 + 1) : false;
          $out = str_replace('~', '', $out);
          $out_len = strlen($out);
          if ($out == '') {
            $type = ($pos1 === 0) ? VKLang::PATTERN_END : (($pos1 == $pat_len) ? VKLang::PATTERN_START : VKLang::PATTERN_EXACT);
            $out = $search;
          } else if ($pos1 === false) {
            $type = VKLang::PATTERN_EXACT;
            if ($pos3 === false) {
              // $out = $out;
            } else if ($pos3 === 0 && $pos4 === 1) { // replace: ~~replacetext
              $out = $search.$out;
            } else if ($pos3 === 0 && $pos4 === false) { // replace: ~replacetext
              $out = $search.$out;
            } else if ($pos3 == $out_len && $pos4 === false) { // replace: replacetext~
              $out = $out.$search;
            } else if ($pos3 == $out_len && $pos4 == $out_len + 1) { // replace: replacetext~~
              $out = $out.$search;
            } else {
              var_dump($rule, $search, $out, $pos1, $pos2, $pos3, $pos4);
              return false; // parse error
            }
          } else if ($pos1 === 0) {
            $type = VKLang::PATTERN_END;
            if ($pos2 === false) { // search: ~searchtext
              if ($pos3 === 0 && $pos4 === 1) { // replace: ~~replacetext
                $out = $search.$out;
              } else if ($pos3 === 0 && $pos4 === false) { // replace: ~replacetext
                // $out = $out;
              } else {
                var_dump($rule, $search, $out, $pos1, $pos2, $pos3, $pos4);
                return false; // parse error
              }
            } else { // search: ~search~text
              if ($pos3 === 0 && $pos4 === 1) { // replace: ~~replacetext
                $out = $search.$out;
              } else if ($pos3 === 0 && $pos4 === false) { // replace: ~replacetext
                $out = substr($search, 0, $pos2 - 1).$out;
              } else {
                var_dump($rule, $search, $out, $pos1, $pos2, $pos3, $pos4);
                return false; // parse error
              }
            }
          } else {
            $type = VKLang::PATTERN_START;
            if ($pos2 === false) {
              if ($pos1 == $pat_len) { // search: searchtext~
                $pos = 0;
                if ($pos3 == $out_len && $pos4 === false) { // replace: replacetext~
                  // $out = $out;
                } else if ($pos3 == $out_len && $pos4 == $out_len + 1) { // replace: replacetext~~
                  $out = $out . $search;
                } else {
                var_dump($rule, $search, $out, $pos1, $pos2, $pos3, $pos4);
                  return false; // parse error
                }
              } else { // search: search~text
                $type = VKLang::PATTERN_EXACT;
                if ($pos3 === false) {
                  // $out = $out;
                } else if ($pos3 === 0 && $pos4 === 1) { // replace: ~~replacetext
                  $out = $search.$out;
                } else if ($pos3 === 0 && $pos4 === false) { // replace: ~replacetext
                  $out = substr($search, 0, $pos1).$out;
                } else if ($pos3 == $out_len && $pos4 === false) { // replace: replacetext~
                  $out = $out.substr($search, $pos1);
                } else if ($pos3 == $out_len && $pos4 == $out_len + 1) { // replace: replacetext~~
                  $out = $out . $search;
                } else {
                var_dump($rule, $search, $out, $pos1, $pos2, $pos3, $pos4);
                  return false; // parse error
                }
              }
            } else {
              if ($pos2 == $pat_len + 1) { // search: search~text~
                if ($pos3 == $out_len && $pos4 === false) { // replace: replacetext~
                  $out = $out.substr($search, $pos1);
                } else if ($pos3 == $out_len && $pos4 == $out_len + 1) { // replace: replacetext~~
                  $out = $out . $search;
                } else {
                var_dump($rule, $search, $out, $pos1, $pos2, $pos3, $pos4);
                  return false; // parse error
                }
              } else { // search: sea~rh~text
                $type = VKLang::PATTERN_EXACT;
                if ($pos3 === false) {
                  // $out = $out;
                } else if ($pos3 === 0 && $pos4 === 1) { // replace: ~~replacetext
                  $out = $search.$out;
                } else if ($pos3 === 0 && $pos4 === false) { // replace: ~replacetext
                  $out = substr($search, 0, $pos2 - 1).$out;
                } else if ($pos3 == $out_len && $pos4 === false) { // replace: replacetext~
                  $out = $out.substr($search, $pos1);
                } else if ($pos3 == $out_len && $pos4 == $out_len + 1) { // replace: replacetext~~
                  $out = $out . $search;
                } else {
                var_dump($rule, $search, $out, $pos1, $pos2, $pos3, $pos4);
                  return false; // parse error
                }
              }
            }
          }
          if (!$pat_len) {
            $letter = '~';
          } else {
            $letter = $type == VKLang::PATTERN_EXACT ? '~~' : ($type == VKLang::PATTERN_START ? ($search[0].'~') : (VKLang::PATTERN_END ? '~'.$search[$pat_len - 1] : '~'));
            
            if ($letter === '') $letter = '~';
          }
          $patterns[$key] = array(isset($replace['cond']) ? intval($conditions[$replace['cond']]) : 0, $type, $pat_len, $search, $out, $letter);
        }
      }
    }
    krsort($patterns);
    $rule['patterns'] = array();
    foreach ($patterns as $pattern) {
      if ($pattern[5] == '~~') {
        //if (!in_array($pattern[4], $rule['patterns'][$pattern[5]][$pattern[3]])) {
          $rule['patterns'][$pattern[5]][$pattern[3]][] = $pattern[0];
          $rule['patterns'][$pattern[5]][$pattern[3]][] = $pattern[4];
        //}
      } else if ($pattern[5] == '~') {
        $rule['patterns'][$pattern[5]][] = $pattern[0];
        $rule['patterns'][$pattern[5]][] = $pattern[1];
        $rule['patterns'][$pattern[5]][] = $pattern[4];
      } else {
        //if (!in_array($pattern[4], $rule['patterns'][$pattern[5]])) {
          $rule['patterns'][$pattern[5]][] = $pattern[0];
          //$rule['patterns'][$pattern[5]][] = $pattern[1];
          $rule['patterns'][$pattern[5]][] = $pattern[2];
          $rule['patterns'][$pattern[5]][] = $pattern[3];
          $rule['patterns'][$pattern[5]][] = $pattern[4];
        //}
      }
    }
    foreach ($rule['patterns'] as $letter => &$pattern) {
      if ($letter == '~~') {
        foreach ($pattern as $name => $replace) {
          $pattern[$name] = implode(',', $replace);
        }
      } else {
        $rule['patterns'][$letter] = implode(',', $pattern);
      }
    }
    //$rule['patterns'] = array_values($patterns);
  }
  return $rule;
}



static function toEnc($text) {
  if (VKLANG_ENCODING == 'UTF-8') return $text;
  if (VKLANG_HTML_ENCODING) {
    return html_entity_decode(mb_convert_encoding(htmlspecialchars($text, ENT_NOQUOTES), 'HTML-ENTITIES', 'UTF-8'), ENT_NOQUOTES, VKLANG_ENCODING);
  } else {
    return mb_convert_encoding($text, VKLANG_ENCODING, 'UTF-8');
  }
}



static function serializeArray($array, $depth = 0) {
  
  $items = array();
  $res = "";
  $tab = str_repeat(self::CONF_TAB, $depth);
  
  if (is_array($array)) {
    $is_assoc = (is_array($array) && 0 !== count(array_diff_key($array, array_keys(array_keys($array)))));
    $res .= "array(".($is_assoc?"\n":"");
    $has_cr = false;
    if (count($array) > 0) {
      foreach ($array as $key => $val) {
        $serVal = self::serializeArray($val, $depth+1);
        if ($is_assoc) {
          $k = (is_numeric($key) && intval($key) == $key) ? intval($key) : "'".addslashes($key)."'";
          $res .= $tab.self::CONF_TAB."{$k} => ".$serVal.",\n";
        } else {
          if (strpos($serVal, "\n") !== false || strlen($serVal) > 40 || ($has_cr && strlen($serVal) > 20)) {
            $has_cr = true;
            if ($key == 0) {
              $res .= "\n";
            }
            $res .= $tab.self::CONF_TAB.$serVal.",\n";
          } else {
            $res .= $serVal.", ";
          }
        }
      }
      $res = substr($res, 0, -2).($is_assoc?"\n":"");
    }
    $res .= ($is_assoc?$tab:"").")";
  } else if (is_string($array)) {
    $res .= "'".addslashes($array)."'";
  } else if (is_numeric($array)) {
    $res .= $array;
  } else {
    $res .= 'null';
  }
  
  return $res;
}


}